#pragma once

#include "SocketServer.h"
#include "IActiveObject.h"
#include "PortIndex.h"
#include "EndPointMap.h"
#include "Binding.h"
#include "SimpleSocket.h"
#include "PortRouterMessage.h"

namespace RoomFW {

class PortRouterMessage;

/// Routes ROOM messages to/from remote ports.
///
/// @author Andrew Sage
class PortRouter : public virtual SocketServer, public virtual IActiveObject
{
public:

	/// Get the single instance of this class.
	///
	/// @return Reference to the single instance of this class.
	static PortRouter& Instance(bool reset=false);

	virtual bool start(int port, const char* address, uint32_t timeout = INFINITE);
	virtual SimpleSocket::SocketPtr connect(int port, const char* address, unsigned attempts = 10, unsigned attemptIntervalMsec = 1000);

	/// Register and unregister a port with remote DynamicBindingManager.
	void registerPort(const char* portName, IContractCheckerSPC contract);
	void unregisterPort(const char* portName);

	/// Notify other side that message went out of scope without a reply.
	void notifyNoReply(int replyId);

protected:

	virtual bool handleConnect(SimpleSocket::SocketPtr /*client*/);
	virtual bool handleDisconnect(SimpleSocket::SocketPtr /*client*/);
	virtual bool handleMessage(SimpleSocket::SocketPtr client, const std::string& /*message*/);

	virtual const char* getObjectName(void) const {return "PortRouter";}
	virtual const char* getPortName(size_t portIndex) const {return "PortRouterVirtualPort";}
	virtual IEndPointSP getEndPoint(PortIndex::PrimaryIndex portIndex);
	virtual bool setEndPoint(PortIndex& portIndex, IEndPointSP endPoint);
	virtual bool clearEndPoint(const PortIndex& portIndex);
	virtual IContractCheckerSPC getContractChecker(PortIndex portIndex) const;
	virtual void put(RoomMessageSP message);
	virtual size_t timerPortIndex() const {return 0;}

private:

	/// Send a message.
	void sendMessage(SimpleSocket::SocketPtr sock, const PortRouterMessage& msg);

	/// Synchronous.  Only call from public interface (caller thread).
	template<typename REPLY_T>
	bool sendMessage(SimpleSocket::SocketPtr sock, const PortRouterMessage& msg, REPLY_T& reply);


	/// Handle incoming messages.
	friend class PRM_Connect;
	virtual void dispatch(PRM_Connect* msg);

	friend class PRM_Register;
	virtual void dispatch(PRM_Register* msg);

	friend class PRM_Unregister;
	virtual void dispatch(PRM_Unregister* msg);

	friend class PRM_Bind;
	virtual void dispatch(PRM_Bind* msg);

	friend class PRM_Bind_Ack;
	virtual void dispatch(PRM_Bind_Ack* msg);

	friend class PRM_Unbind;
	virtual void dispatch(PRM_Unbind* msg);

	friend class PRM_Send;
	virtual void dispatch(PRM_Send* msg);

	friend class PRM_NoReply;
	virtual void dispatch(PRM_NoReply* msg);

	/// We need to synchronize access to anything used by public interface.
	/// Basically, we'll guard any member data access, but make sure no calls that
	/// could come back to a guard are outside the guard.
	mutable SimpleCriticalSection cs_;


	/// Client connection info.
	//@{
	/// Set by PortRouter::start and used by PortRouter::handleConnect
	int localPort_;
	std::string localAddress_;
	//@}

	/// Set by PortRouter::handleMessage and used by PortRouter::dispatch(PRM_Register* msg)
	/// and PortRouter::dispatch(PRM_Bind* msg)
	SimpleSocket::SocketPtr replySock_;


	typedef std::set<SimpleSocket::SocketPtr> SocketSet;
	SocketSet clients_;
	SocketSet pendingConnections_;

	/// Next virtual port index.
	/// All of our virtual ports are bound locally.
	/// we must have in/out end points for each.
	PortIndex::PrimaryIndex nextPortIndex_;
	typedef std::set<PortIndex::PrimaryIndex> PortIndexSet;
	PortIndexSet recycledPorts_;


	/// Contract info for each of our virtual ports.  Doesn't matter if port was created
	/// due to registration or binding.  When we recieve a registration, we create a
	/// port and register it.  When we recieve a binding, we create a port and bind it.
	typedef std::map<PortIndex::PrimaryIndex, IContractCheckerSPC> PortIndexContractMap;
	PortIndexContractMap portIndexContractMap_;


	/// Contract info for just those ports registered by local objects that need to be
	/// registered with Remote Counterpart (RCP).
	typedef std::pair<ContractTypeId, bool> ContractInfo;				// bool indicates !isConjugate (flipped for remote end).
	typedef std::map<std::string, ContractInfo> PortNameContractMap;	// string is portName
	PortNameContractMap localRegistrations_;

	/// Ports names registered remotely.
	struct RemoteRegistration
	{
		SimpleSocket::SocketPtr socket;
		std::string name;
	};
	typedef std::map<PortIndex::PrimaryIndex, RemoteRegistration> PortRegistrationMap;
	PortRegistrationMap remoteRegistrations_;

	/// Remote Bindings
	struct RemoteBinding
	{
		SimpleSocket::SocketPtr socket;
		PortIndex portIndex; /// remote
		BindingSP binding;
	};
	typedef std::map<PortIndex, RemoteBinding> PortIndexMap; /// local port to remote binding
	PortIndexMap remoteBindings_;


	/// Endpoints to local objects.
	EndpointMap localEndPoints_;

	/// Endpoints to remote objects.  We actually, they point to us, but we
	/// forward the messages.
	EndpointMap remoteEndPoints_;


	/// Map reply ids to local endpoints.
	typedef std::map<int, IEndPointSP> ReplyMap;
	ReplyMap replyMap_;

	typedef ::boost::shared_ptr<PRM_Bind_Ack> Ptr;

	Ptr bindReply_;
	SimpleEvent bindAckEvent_;

	typedef std::list<PortRouterMessage::Ptr> PortRouterMessageList;
	PortRouterMessageList todoList_;
	SimpleEvent bindAckWait_;

	int nextReplyId();


	/// Enforce that this class is a singleton.
	PortRouter();
	~PortRouter();
};

}
